﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq.Expressions;
using Accountant.DataModel;
using Accountant.MySerializer;
using System.Linq;

namespace Accountant.Utils
{
  public static class Utils
  {
    public static decimal? Convert(this decimal value, Currency from, Currency to, DateTime at)
    {
      // порядок арифметических операций теоретически влияет на точность
      var direct = from.Context.ExchangeRates.LastOrDefault(
        r => r.CurrencyFrom == from && r.CurrencyTo == to);
      if (direct != null) return (direct.Scale * value) / direct.Rate;

      var invert = from.Context.ExchangeRates.LastOrDefault(
        r => r.CurrencyFrom == to && r.CurrencyTo == from);
      if (invert != null) return (invert.Rate * value) / invert.Scale;

      Currency firstCurrency;
      var directFirst = from.Context.ExchangeRates.
        LastOrDefault(r => r.CurrencyFrom == from);
      if (directFirst != null)
      {
        value = (value * directFirst.Scale) / directFirst.Rate;
        firstCurrency = directFirst.CurrencyTo;
      }
      else
      {
        var inverseFirst = from.Context.ExchangeRates.
          LastOrDefault(r => r.CurrencyTo == from);
        if (inverseFirst == null) return null;
        value = (value*inverseFirst.Rate)/inverseFirst.Scale;
        firstCurrency = inverseFirst.CurrencyFrom;
      }

      var directSecond = from.Context.ExchangeRates.LastOrDefault(
        r => r.CurrencyFrom == firstCurrency && r.CurrencyTo == to);
      if (directSecond != null) return (value * directSecond.Scale) / directSecond.Rate;

      var invertSecond = from.Context.ExchangeRates.LastOrDefault(
        r => r.CurrencyFrom == to && r.CurrencyTo == firstCurrency);
      if (invertSecond != null) return (value * invertSecond.Rate) / invertSecond.Scale;

      return null;
    }

    public static void Merge<T>(this IEnumerable<T> x, IEnumerable<T> y, Action<T, T> merge)
    {
      var ex = x.GetEnumerator();
      var ey = y.GetEnumerator();
      while (true)
      {
        var bx = ex.MoveNext();
        var by = ey.MoveNext();
        if (bx != by) throw new InvalidOperationException("Collections have different sie");
        if (!bx) return;
        merge(ex.Current, ey.Current);
      }
    }
    public static string GetLambdaExpressionName(this Expression expression)
    {
      MemberExpression memberExp = expression as MemberExpression;

      if (memberExp == null)
      {
        // Check if it is an UnaryExpression and unwrap it
        var unaryExp = expression as UnaryExpression;
        if (unaryExp != null)
          memberExp = unaryExp.Operand as MemberExpression;
      }

      if (memberExp == null)
        throw new ArgumentException("Cannot get name from expression", "expression");

      return memberExp.Member.Name;
    }

    /// <summary>
    /// CurrencyFrom*ExchangeRate = CurrencyTo;
    /// </summary>
    /// <param name="rates"></param>
    /// <param name="startAt"></param>
    /// <param name="currencyFrom"></param>
    /// <param name="currencyTo"></param>
    /// <param name="exhangeRate"></param>
    internal static void AddRate(this DbSet<ExchangeRate, AccountantContext> rates,
      DateTime startAt, Currency currencyFrom, Currency currencyTo, double exhangeRate)
    {
      var referenceCurrency = currencyFrom.Context.Currencies[0];
      
    }
  }
}